package cn.itrip.auth.service;

import cn.itrip.auth.exception.RedisException;
import cn.itrip.auth.exception.TokenException;
import cn.itrip.auth.exception.TokenGenerateException;
import cn.itrip.auth.exception.TokenTimeProtectedException;
import cn.itrip.beans.pojo.ItripUser;
import cn.itrip.beans.vo.ItripTokenVO;
import cn.itrip.common.MD5;
import cn.itrip.common.RedisAPI;
import cn.itrip.common.UserAgentUtil;
import cn.itrip.common.ValidationToken;
import com.alibaba.fastjson.JSON;
import cz.mallat.uasparser.UserAgentInfo;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * @Author: jxm
 * @Description:
 * @Date: 2020/3/12 16:13
 * @Version: 1.0
 */
@Service("tokenService")
public class TokenServiceImpl implements TokenService {

    @Resource
    private RedisAPI redisAPI;
    @Resource
    private ValidationToken validationToken;
    /**
     *Token组成：
     * 前缀:token
     * 终端类型：PC/MOBILE
     * userCode信息：（MD5加密） 32 位
     * userID:来自数据库
     * Token创建时间：yyyyMMddHHmmss
     * 最后6位：根据user-Agent MD5加密 ：6位验证码
     */
    @Override
    public String generateToken(String agent, ItripUser user) throws Exception {
        UserAgentInfo info = UserAgentUtil.getUasParser().parse(agent);
        String deviceType = info.getDeviceType();
        StringBuilder builder = new StringBuilder("token:");

        if(deviceType.equals("Personal computer")){//Personal computer --> PC端
            builder.append("PC-");
        }else if(deviceType.equals(UserAgentInfo.UNKNOWN)){ //MOBIL or Anknown
            if(UserAgentUtil.CheckAgent(agent)){
                builder.append("MOBILE-");
            }else {
                builder.append("PC-");
            }
        }else{ //MOBILE
            builder.append("MOBILE-");
        }
        builder.append(MD5.getMd5(user.getUserCode(),32));
        builder.append("-");
        builder.append(user.getId());
        builder.append("-");
        builder.append(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));
        builder.append("-");
        builder.append(MD5.getMd5(agent,6));
        return builder.toString();
    }

    //第二种生成token方法
    @Override
    public ItripTokenVO generateToken2(String agent, ItripUser user) throws Exception {
        UserAgentInfo info = UserAgentUtil.getUasParser().parse(agent);
        String deviceType = info.getDeviceType();
        StringBuilder builder = new StringBuilder("token:");
        if(deviceType.equals("Personal computer")) {
            builder.append("PC-");
        }else if(deviceType.equals(UserAgentInfo.UNKNOWN)){ //MOBIL or Anknown
            if(UserAgentUtil.CheckAgent(agent)){
                builder.append("MOBILE-");
            }else {
                builder.append("PC-");
            }
        }else{ //MOBILE
            builder.append("MOBILE-");
        }
        builder.append(MD5.getMd5(user.getUserCode(),32));
        builder.append("-");
        builder.append(user.getId());
        builder.append("-");
        Date date = new Date();
        builder.append(new SimpleDateFormat("yyyyMMddHHmmss").format(date));
        builder.append("-");
        builder.append(MD5.getMd5(agent,6));
        ItripTokenVO vo = new ItripTokenVO(builder.toString(),
                date.getTime()+TokenService.TOKEN_TIMEOUT,date.getTime());
        return vo;
    }

    @Override
    public boolean saveToken(String token, ItripUser user) throws Exception {
        if(token.startsWith("token:PC-")){
            return redisAPI.set(token,TOKEN_TIMEOUT,JSON.toJSONString(user));
        }else{
            return redisAPI.set(token,JSON.toJSONString(user));
        }

    }

    @Override
    public boolean validateToken(String token, String agent) throws Exception {
        return validationToken.validateToken(token,agent);
    }

    @Override
    public void deleteToken(String token) throws Exception {
        redisAPI.delete(token);
    }

    @Override
    public ItripTokenVO refreshToken(String token, String agent)
            throws TokenException, TokenTimeProtectedException, ParseException, TokenGenerateException, RedisException {
        //有效验证
        if(!redisAPI.exist(token)){
            throw new TokenException("无效token");
        }
        //不能太频繁，是否达到保护器
        Date genDate = new SimpleDateFormat("yyyyMMddHHmmss")
                .parse(token.split("-")[3]);
        if((Calendar.getInstance().getTimeInMillis()-genDate.getTime())
                < TokenService.TOKEN_REPLACEMENT_PROTECTED_TIME){
            throw new TokenTimeProtectedException("还在保护期，不能置换");
        }
        //生成新token
        String jsonUser = redisAPI.get(token);
        ItripUser user = JSON.parseObject(jsonUser,ItripUser.class);
        ItripTokenVO newToken = null;
        try {
            newToken= this.generateToken2(token,user);
        } catch (Exception e) {
            e.printStackTrace();
            throw new TokenGenerateException("token创建失败",e);
        }
        //token数据移交
        try {
            this.saveToken(newToken.getToken(),user);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RedisException("token保存失败");
        }
        //旧token续命2分钟
        redisAPI.set(token,TokenService.TOKEN_DEPLA_TIME,jsonUser);

        //分装vo对象
        return newToken;
    }
}
